\section{命令行环境}
\subsection{任务控制：pgrep和pkill}
\begin{questionbox}
    我们可以使用类似 ps aux | grep 这样的命令来获取任务的 pid ，然后您可以基于 pid 来结束这些进程。但我们其实有更好的方法来做这件事。在终端中执行 sleep 10000 这个任务。然后用 Ctrl-Z 将其切换到后台并使用 bg 来继续允许它。现在，使用 pgrep 来查找 pid 并使用 pkill 结束进程而不需要手动输入 pid。(提示：: 使用 -af 标记)。
\end{questionbox}
我们可以按照如下的步骤更好的管理进程。

首先，我们在终端执行\texttt{sleep 10000}，然后用\texttt{Ctrl-Z }暂停它，再用\texttt{bg}让它在后台继续运行。具体操作如图\ref{sleep 10000}
\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-12 114202.png}
	\caption{sleep 10000}
	\label{sleep 10000}
\end{figure}
接着我们可以使用\texttt{pgerp}来查找进程的\texttt{pid}。我们可以输入如下命令
\begin{lstlisting}
pgrep -af "sleep 10000"
\end{lstlisting}
由于\texttt{sleep 10000}是完整的命令行，我们需要使用\texttt{-f}选项来匹配完整的命令行（而不仅仅是进程名）。\texttt{-a}命令的作用则是显示完整的命令行。
最后我们可以直接使用\texttt{pkill}来结束这个进程，而无需手动查找和输入\texttt{pid}。
\begin{lstlisting}
    pkill -f "sleep 10000" #-f:表示根据完整的命令行来匹配进程
\end{lstlisting}

具体操作过程如图\ref{pgrep和pkill的使用}
\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-12 114806.png}
	\caption{pgrep和pkill的使用}
	\label{pgrep和pkill的使用}
\end{figure}
我们在\texttt{pkill}命令后使用\texttt{pgrep}命令来查看进程是否结束，发现终端没有输出，这说明进程已经结束了。

\subsection{任务控制：多个任务同时运行}
\begin{questionbox}
如果您希望某个进程结束后再开始另外一个进程， 应该如何实现呢？在这个练习中，我们使用 sleep 60 \& 作为先执行的程序。一种方法是使用 wait 命令。尝试启动这个休眠命令，然后待其结束后再执行 ls 命令。

但是，如果我们在不同的 bash 会话中进行操作，则上述方法就不起作用了。因为 wait 只能对子进程起作用。之前我们没有提过的一个特性是，kill 命令成功退出时其状态码为 0 ，其他状态则是非 0。kill -0 则不会发送信号，但是会在进程不存在时返回一个不为 0 的状态码。请编写一个 bash 函数 pidwait ，它接受一个 pid 作为输入参数，然后一直等待直到该进程结束。您需要使用 sleep 来避免浪费 CPU 性能。
\end{questionbox}

我们首先尝试休眠命令：
\begin{lstlisting}
sleep 60 &    # 启动后台休眠任务
PID=$!        # $! 是最近一个后台进程的 PID
echo "Started sleep with PID: $PID"
wait $PID     # 等待该进程结束
ls            # 在 sleep 结束后执行 ls
\end{lstlisting}
结果如图\ref{启动休眠命令后ls}
\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-14 142427.png}
	\caption{启动休眠命令后ls}
	\label{启动休眠命令后ls}
\end{figure}

接下来我们就需要按照问题要求编写\texttt{bash}函数\texttt{pidwait}了。我们输入如下的内容：
\begin{lstlisting}
pidwait() {
    local target_pid="$1"

    # 参数校验
    if [[ -z "$target_pid" ]]; then
        echo "Usage: pidwait <PID>" >&2
        return 1
    fi

    # 检查是否为数字
    if ! [[ "$target_pid" =~ ^[0-9]+$ ]]; then
        echo "Error: '$target_pid' is not a valid PID (must be a positive integer)." >&2
        return 1
    fi

    # 循环检测目标进程是否存在
    while kill -0 "$target_pid" 2>/dev/null; do
        sleep 0.5  # 避免过度占用 CPU，适当延时
    done

    # 当循环退出时，说明进程已终止
    echo "Process $target_pid has terminated."
}
\end{lstlisting}
启动脚本然后按照问题要求进行测试，结果如图\ref{pidwait命令}
\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-14 143912.png}
	\caption{pidwait命令}
	\label{pidwait命令}
\end{figure}

\subsection{\texttt{tmux}使用练习}
启动一个新的会话：\texttt{tmux}或者起个名字\texttt{tmux new-session -s mywork}

\texttt{tmux}常用命令：
\begin{itemize}
    \item \texttt{Ctrl+b} 然后按 \texttt{c}：创建一个新窗口（window）
    \item \texttt{Ctrl+b} 然后按 \texttt{n}：切换到下一个窗口
    \item \texttt{Ctrl+b} 然后按 \texttt{p}：切换到上一个窗口
    \item \texttt{Ctrl+b} 然后按 \texttt{0-9}：跳转到第 0~9 个窗口
    \item \texttt{Ctrl+b} 然后按 \texttt{\%}：左右分屏（垂直分割）
    \item \texttt{Ctrl+b} 然后按 \texttt{"}：上下分屏（水平分割）
    \item \texttt{Ctrl+b} 然后按 \texttt{$\uparrow\downarrow\leftarrow\rightarrow$}：在面板（pane）之间切换
    \item \texttt{Ctrl+b} 然后按 \texttt{d}：脱离会话（detach），回到原终端，但 tmux 继续运行！
    \item \texttt{Ctrl+b} 然后按 \texttt{\&}：关闭当前窗口（确认）
    \item \texttt{Ctrl+b} 然后按 \texttt{x}：杀掉当前面板（确认）
    \item \texttt{Ctrl+b} 然后按 \texttt{z}：缩放当前面板（全屏/恢复）
\end{itemize}

部分命令联系结果如图\ref{tmux练习}
\begin{figure}[htb]
	\centering
	\includegraphics[width=0.48\linewidth]{assets/images/屏幕截图 2025-09-14 145352.png} \hfill
	\includegraphics[width=0.48\linewidth]{assets/images/屏幕截图 2025-09-14 145352.png}
	\caption{tmux练习}
	\label{tmux练习}
\end{figure}

\subsection{别名}
\begin{questionbox}
创建一个 dc 别名，它的功能是当我们错误的将 cd 输入为 dc 时也能正确执行。\\
执行 history | awk '{$1="";print substr($0,2)}' | sort | uniq -c | sort -n | tail -n 10 来获取您最常用的十条命令，尝试为它们创建别名。注意：这个命令只在 Bash 中生效，如果您使用 ZSH，使用 history 1 替换 history。
\end{questionbox}

第一个问题十分简单，我们只需要在终端中输入如下命令即可实现：
\begin{lstlisting}
alias dc='cd'
\end{lstlisting}

当然，这样的操作是一次性的，如果我们想要每次登陆的时候都让这个操作保存生效我们就需要将这个命令写入\texttt{bash}的配置文件了。
\begin{lstlisting}
    echo "alias dc='cd'" >> ~/.bashrc
    source ~/.bashrc  #这个命令是为了让配置文件生效
\end{lstlisting}

结果如图\ref{dc别名}
\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-14 150455.png} \\[2ex]
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-14 150501.png}
	\caption{dc别名}
	\label{dc别名}
\end{figure}

有了前面一个问题的铺垫，接下来的这个问题也比较简单，操作结果如图\ref{常用的十个命令}
\begin{figure}[H]
	\centering
	\includegraphics[width=\linewidth]{assets/images/常用的十个命令.png}
	\caption{常用的十个命令}
	\label{常用的十个命令}
\end{figure}

\subsection{远端设备}
\begin{questionbox}
前往 ~/.ssh/ 并查看是否已经存在 SSH 密钥对。如果不存在，请使用 ssh-keygen -o -a 100 -t ed25519 来创建一个。建议为密钥设置密码然后使用 ssh-agent，更多信息可以参考 这里；
在 .ssh/config 加入下面内容：
\begin{lstlisting}
Host vm
    User username\_goes\_here
    HostName ip\_goes\_here
    IdentityFile ~/.ssh/id\_ed25519
    LocalForward 9999 localhost:8888
\end{lstlisting}
使用 ssh\-copy\-id vm 将您的 ssh 密钥拷贝到服务器。
使用 python -m http.server 8888 在您的虚拟机中启动一个 Web 服务器并通过本机的 http://localhost:9999 访问虚拟机上的 Web 服务器
使用 sudo vim /etc/ssh/sshd\_config 编辑 SSH 服务器配置，通过修改 PasswordAuthentication 的值来禁用密码验证。通过修改 PermitRootLogin 的值来禁用 root 登录。然后使用 sudo service sshd restart 重启 ssh 服务器，然后重新尝试
\end{questionbox}

由于我的虚拟机中没有\texttt{.ssh}目录，所以我们首先在主目录中创建\texttt{.sh}文件夹，使用\texttt{mkdir ~/.ssh}，然后给他设置对应的权限，使用\texttt{chmod 700 ~/.ssh}，这里我们就完成了第一步。

然后我们就需要生成ssh秘钥对了，这里我们使用的是现代的，安全的ed25519算法。
\begin{lstlisting}
    ssh-keygen -t ed25519 -C "13409874042@163.com"
\end{lstlisting}
后面的过程我们可以直接回车选择默认路径和空密码。然后使用\texttt{ls -l ~/.ssh/}来验证我们的秘钥有没有生成。

最后我们可以通过\texttt{cat ~/.ssh/id\_ed25519.pub}来查看公钥的内容用于复制到服务器或者github上。

具体结果和流程如图\ref{远端设备}
\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-16 100855.png}
	\caption{远端设备}
	\label{远端设备}
\end{figure}